/**
 * @file ipaddr6_test.c
 * @author Ambroz Bizjak <ambrop7@gmail.com>
 * 
 * @section LICENSE
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the author nor the
 *    names of its contributors may be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <inttypes.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>

#include <misc/debug.h>
#include <misc/ipaddr6.h>

#define PRINT_TEST(addr_bytes, string) \
    { \
        struct ipv6_addr addr = {addr_bytes}; \
        char str[IPADDR6_PRINT_MAX]; \
        ipaddr6_print_addr(addr, str); \
        ASSERT_FORCE(!strcmp(str, (string))); \
        struct ipv6_addr parsed_addr; \
        int res = ipaddr6_parse_ipv6_addr(MemRef_MakeCstr(str), &parsed_addr); \
        ASSERT_FORCE(res); \
        ASSERT_FORCE(!memcmp(parsed_addr.bytes, addr.bytes, 16)); \
    }

#define PARSE_TEST(string, addr_bytes) \
    { \
        struct ipv6_addr exp_addr = {addr_bytes}; \
        struct ipv6_addr addr; \
        int res = ipaddr6_parse_ipv6_addr(MemRef_MakeCstr((string)), &addr); \
        ASSERT_FORCE(res); \
        ASSERT_FORCE(!memcmp(addr.bytes, exp_addr.bytes, 16)); \
    }

#define PARSE_FAIL_TEST(string) \
    { \
        struct ipv6_addr addr; \
        int res = ipaddr6_parse_ipv6_addr(MemRef_MakeCstr((string)), &addr); \
        ASSERT_FORCE(!res); \
    }

#define MASK_TEST(mask_bytes, prefix) \
    { \
        struct ipv6_addr mask = {mask_bytes}; \
        int parsed_prefix; \
        int res = ipaddr6_ipv6_prefix_from_mask(mask, &parsed_prefix); \
        ASSERT_FORCE(res); \
        ASSERT_FORCE(parsed_prefix == (prefix)); \
        struct ipv6_addr generated_mask; \
        ipaddr6_ipv6_mask_from_prefix(parsed_prefix, &generated_mask); \
        ASSERT_FORCE(!memcmp(generated_mask.bytes, mask.bytes, 16)); \
    }

#define PASS(...) __VA_ARGS__

int main ()
{
    PRINT_TEST(PASS({0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}), "::1")
    PRINT_TEST(PASS({0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), "::")
    PRINT_TEST(PASS({0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}),"2001:db8::1")
    PRINT_TEST(PASS({0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01}), "2001:db8::2:1")
    PRINT_TEST(PASS({0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01}), "2001:db8:0:1:1:1:1:1")
    PRINT_TEST(PASS({0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01}), "2001:db8:0:1:1:1:1:1")
    PRINT_TEST(PASS({0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}), "2001:db8::1:0:0:1")
    
    PARSE_TEST("::", PASS({0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}))
    PARSE_TEST("::1", PASS({0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}))
    PARSE_TEST("::abcd", PASS({0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xab, 0xcd}))
    PARSE_TEST("::0123:abcd", PASS({0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x23, 0xab, 0xcd}))
    PARSE_TEST("abcd::", PASS({0xab, 0xcd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}))
    PARSE_TEST("abcd:0123::", PASS({0xab, 0xcd, 0x01, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}))
    PARSE_TEST("1::2", PASS({0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}))
    PARSE_TEST("abcd:0123::3210:dcba", PASS({0xab, 0xcd, 0x01, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x10, 0xdc, 0xba}))
    PARSE_TEST("4567:abcd:0123::3210:dcba", PASS({0x45, 0x67, 0xab, 0xcd, 0x01, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x10, 0xdc, 0xba}))
    PARSE_TEST("4567:abcd:0123:1111:2222:3333:3210::", PASS({0x45, 0x67, 0xab, 0xcd, 0x01, 0x23, 0x11, 0x11, 0x22, 0x22, 0x33, 0x33, 0x32, 0x10, 0x00, 0x00}))
    PARSE_TEST("::4567:abcd:0123:1111:2222:3333:3210", PASS({0x00, 0x00, 0x45, 0x67, 0xab, 0xcd, 0x01, 0x23, 0x11, 0x11, 0x22, 0x22, 0x33, 0x33, 0x32, 0x10}))
    PARSE_TEST("4567:abcd:0123:1111:2222:3333:3210:dcba", PASS({0x45, 0x67, 0xab, 0xcd, 0x01, 0x23, 0x11, 0x11, 0x22, 0x22, 0x33, 0x33, 0x32, 0x10, 0xdc, 0xba}))
    PARSE_TEST("04567:000abcd:00000123:01111:2222:03333:0003210:0dcba", PASS({0x45, 0x67, 0xab, 0xcd, 0x01, 0x23, 0x11, 0x11, 0x22, 0x22, 0x33, 0x33, 0x32, 0x10, 0xdc, 0xba}))
    PARSE_TEST("::1.2.3.4", PASS({0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04}))
    PARSE_TEST("ff::1.2.3.4", PASS({0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04}))
    PARSE_TEST("ff::0.2.3.4", PASS({0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x04}))
    PARSE_TEST("1:2:3:4:5:6:1.2.3.4", PASS({0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00, 0x06, 0x01, 0x02, 0x03, 0x04}))
    PARSE_TEST("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255", PASS({0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}))
    PARSE_TEST("1::fffa:1.2.3.4", PASS({0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfa, 0x01, 0x02, 0x03, 0x04}))
    PARSE_TEST("1::fffa:0.0.0.0", PASS({0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfa, 0x00, 0x00, 0x00, 0x00}))
    
    PARSE_FAIL_TEST("")
    PARSE_FAIL_TEST(":")
    PARSE_FAIL_TEST("a")
    PARSE_FAIL_TEST("a:b")
    PARSE_FAIL_TEST(":b")
    PARSE_FAIL_TEST("b:")
    PARSE_FAIL_TEST("1:2:3:4:5:6:7")
    PARSE_FAIL_TEST(":1:2:3:4:5:6:7")
    PARSE_FAIL_TEST("1:2:3:4:5:6:7:")
    PARSE_FAIL_TEST(":::")
    PARSE_FAIL_TEST("::a::")
    PARSE_FAIL_TEST("::a::b")
    PARSE_FAIL_TEST("c::a::b")
    PARSE_FAIL_TEST("c::a::")
    PARSE_FAIL_TEST("10000::")
    PARSE_FAIL_TEST("1:2:3:4:5:6:7:8:9")
    PARSE_FAIL_TEST("1:2:3:4::5:6:7:8:9")
    PARSE_FAIL_TEST("::1:2:3:4:5:6:7:8:9")
    PARSE_FAIL_TEST("1:2:3:4:5:6:7:8:9::")
    PARSE_FAIL_TEST("a::b:")
    PARSE_FAIL_TEST(":a::b")
    PARSE_FAIL_TEST("::g")
    PARSE_FAIL_TEST("::1.2")
    PARSE_FAIL_TEST("::1.2.3.4.5")
    PARSE_FAIL_TEST("::01.2.3.4")
    PARSE_FAIL_TEST("::1.2.3.04")
    PARSE_FAIL_TEST("::1.2.3.256")
    PARSE_FAIL_TEST("1.2.3.4")
    PARSE_FAIL_TEST("::8259.2.473.256")
    PARSE_FAIL_TEST("1:2:3:4:5:6:7:1.2.3.4")
    PARSE_FAIL_TEST("1:2:3:4:5:1.2.3.4")
    PARSE_FAIL_TEST("::1.2.3.4::")
    PARSE_FAIL_TEST("::1.2.3.4:1")
    PARSE_FAIL_TEST("localhost6")
    
    MASK_TEST(PASS({0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), 0)
    MASK_TEST(PASS({0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), 1)
    MASK_TEST(PASS({0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), 2)
    MASK_TEST(PASS({0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), 3)
    MASK_TEST(PASS({0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), 4)
    MASK_TEST(PASS({0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), 5)
    MASK_TEST(PASS({0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), 6)
    MASK_TEST(PASS({0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), 7)
    MASK_TEST(PASS({0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), 8)
    
    MASK_TEST(PASS({0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), 9)
    MASK_TEST(PASS({0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), 10)
    MASK_TEST(PASS({0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), 11)
    MASK_TEST(PASS({0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), 12)
    MASK_TEST(PASS({0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), 13)
    MASK_TEST(PASS({0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), 14)
    MASK_TEST(PASS({0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), 15)
    MASK_TEST(PASS({0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), 16)
    
    MASK_TEST(PASS({0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE}), 127)
    MASK_TEST(PASS({0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}), 128)
    
    return 0;
}
